//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<half.cpp>
///	@path	~/src/lib/math/
///	@date	2007/09/29
///	@desc	half -- a 16-bit floating point number class, from ILMBase..

#include "config/config.h"

#include "lib/math/half.h"

namespace xeres {

	const half::uif half::s_toFloat[1 << 16] =
#include "lib/math/table/half_to_float.inl"
	const ushort half::s_eLut[1 << 9] =
#include "lib/math/table/half_e_lut.inl"


	//-----------------------------------------------
	// Overflow handler for float-to-half conversion;
	// generates a hardware floating-point overflow,
	// which may be trapped by the operating system.
	//-----------------------------------------------

	namespace
	{
		class _overflow_generator
		{
		public:
			// ctor
			_overflow_generator( void )
			{
				volatile float f = 1e10;
				for( int i = 0 ; i < 10; i++ )
					f *= f; // this will overflow before
				m_overflow = f;
			}
			// data
			float m_overflow;
		};
	}

	float half::overflow( void )
	{
		static const _overflow_generator s_overflow_generator;
		return s_overflow_generator.m_overflow;
	}

	//-----------------------------------------------------
	// Float-to-half conversion -- general case, including
	// zeroes, denormalized numbers and exponent overflows.
	//-----------------------------------------------------

	short half::convert( int i )
	{
		//
		// Our floating point number, f, is represented by the bit
		// pattern in integer i.  Disassemble that bit pattern into
		// the sign, s, the exponent, e, and the significand, m.
		// Shift s into the position where it will go in in the
		// resulting half number.
		// Adjust e, accounting for the different exponent bias
		// of float and half (127 versus 15).
		//

		register int s =  (i >> 16) & 0x00008000;
		register int e = ((i >> 23) & 0x000000ff) - (127 - 15);
		register int m =   i        & 0x007fffff;

		//
		// Now reassemble s, e and m into a half:
		//

		if (e <= 0)
		{
			if (e < -10)
			{
				//
				// E is less than -10.  The absolute value of f is
				// less than HALF_MIN (f may be a small normalized
				// float, a denormalized float or a zero).
				//
				// We convert f to a half zero with the same sign as f.
				//
				return s;
			}

			//
			// E is between -10 and 0.  F is a normalized float
			// whose magnitude is less than HALF_NRM_MIN.
			//
			// We convert f to a denormalized half.
			//

			//
			// Add an explicit leading 1 to the significand.
			// 

			m = m | 0x00800000;

			//
			// Round to m to the nearest (10+e)-bit value (with e between
			// -10 and 0); in case of a tie, round to the nearest even value.
			//
			// Rounding may cause the significand to overflow and make
			// our number normalized.  Because of the way a half's bits
			// are laid out, we don't have to treat this case separately;
			// the code below will handle it correctly.
			// 

			int t = 14 - e;
			int a = (1 << (t - 1)) - 1;
			int b = (m >> t) & 1;

			m = (m + a + b) >> t;

			//
			// Assemble the half from s, e (zero) and m.
			//

			return s | m;
		}
		else if (e == 0xff - (127 - 15))
		{
			if (m == 0)
			{
				//
				// F is an infinity; convert f to a half
				// infinity with the same sign as f.
				//

				return s | 0x7c00;
			}
			else
			{
				//
				// F is a NAN; we produce a half NAN that preserves
				// the sign bit and the 10 leftmost bits of the
				// significand of f, with one exception: If the 10
				// leftmost bits are all zero, the NAN would turn 
				// into an infinity, so we have to set at least one
				// bit in the significand.
				//

				m >>= 13;
				return s | 0x7c00 | m | (m == 0);
			}
		}
		else
		{
			//
			// E is greater than zero.  F is a normalized float.
			// We try to convert f to a normalized half.
			//

			//
			// Round to m to the nearest 10-bit value.  In case of
			// a tie, round to the nearest even value.
			//

			m = m + 0x00000fff + ((m >> 13) & 1);

			if (m & 0x00800000)
			{
				m =  0;		// overflow in significand,
				e += 1;		// adjust exponent
			}

			//
			// Handle exponent overflow
			//

			if (e > 30)
			{
				overflow ();	// Cause a hardware floating point overflow;
				return s | 0x7c00;	// if this returns, the half becomes an
			}   			// infinity with the same sign as f.

			//
			// Assemble the half from s, e and m.
			//

			return s | (e << 10) | (m >> 13);
		}
	}


} // namespace xeres


#if XERES_MATH_GENERATE_HALF_TABLE

#include <iostream>
#include <iomanip>

namespace xeres {

	using namespace std;

	static unsigned int __half_2_Float( unsigned short y )
	{
		int s = (y >> 15) & 0x00000001;
		int e = (y >> 10) & 0x0000001f;
		int m =  y        & 0x000003ff;
		if (e == 0)
		{
			if (m == 0)
			{
				// Plus or minus zero
				return s << 31;
			}
			else
			{
				// Denormalized number -- renormalize it
				while (!(m & 0x00000400))
				{
					m <<= 1;
					e -=  1;
				}

				e += 1;
				m &= ~0x00000400;
			}
		}
		else if (e == 31)
		{
			if (m == 0)
			{
				// Positive or negative infinity
				return (s << 31) | 0x7f800000;
			}
			else
			{
				// Nan -- preserve sign and significand bits
				return (s << 31) | 0x7f800000 | (m << 13);
			}
		}
		// Normalized number
		e = e + (127 - 15);
		m = m << 13;
		// Assemble s, e and m.
		return (s << 31) | (e << 23) | m;
	}
	// generate
	void half::generate_to_float( void )
	{
		cout.precision( 9 );
		cout.setf( ios_base::hex, ios_base::basefield );
		cout << "{\n    ";
		const int iMax = (1 << 16);
		for( int i = 0 ; i < iMax ; ++i )
		{
			cout << "{0x" << setfill('0') << setw(8) << __half_2_Float(i) << "}, ";
			if( i % 4 == 3 )
			{
				cout << "\n";
				if( i < iMax - 1 )
					cout << "    ";
			}
		}
		cout << "};\n";
	}

	void __init_eLut( unsigned short eLut[] )
	{
		for( int i = 0 ; i < 0x100 ; ++i )
		{
			int e = (i & 0x0ff) - (127 - 15);

			if( e <= 0 || e >= 30 )
			{
				// Special case
				eLut[i]         = 0;
				eLut[i | 0x100] = 0;
			}
			else
			{
				// Common case - normalized half, no exponent overflow possible
				eLut[i]         =  (e << 10);
				eLut[i | 0x100] = ((e << 10) | 0x8000);
			}
		}
	}

	void half::generate_e_lut( void )
	{
		const int tableSize = 1 << 9;
		unsigned short eLut[tableSize];
		__init_eLut( eLut );

		cout << "{\n    ";
		for( int i = 0 ; i < tableSize ; ++i )
		{
			cout << setw(5) << eLut[i] << ", ";
			if( i % 8 == 7 )
			{
				cout << "\n";
				if( i < tableSize - 1 )
					cout << "    ";
			}
		}
		cout << "};\n";
	}

} // namespace xeres

#endif // XERES_MATH_GENERATE_HALF_TABLE
